package com.sky.service.impl;

import com.sky.dto.DataOverViewQueryDTO;
import com.sky.entity.OrderDetail;
import com.sky.entity.Orders;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.WorkspaceService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private WorkspaceService workspaceService;
    @Override
    public TurnoverReportVO turnoverStatistics(DataOverViewQueryDTO dataOverViewQueryDTO) {
        LocalDate begin = dataOverViewQueryDTO.getBegin();
        LocalDate end = dataOverViewQueryDTO.getEnd();
        StringBuilder dataList=new StringBuilder();
        StringBuilder turnoverList=new StringBuilder();
        //遍历起始日期到结束日期中间的日期，计算这些日期的营业额
        for(LocalDate date=begin; !date.isAfter(end);date=date.plusDays(1)) {//注意此处是浅拷贝，date和begin会同步变动！！
            dataList.append(date);
            BigDecimal turnover= calculateTurnover(date);//统计当日营业额
            turnoverList.append(turnover);
            if(!date.equals(end)) {
                dataList.append(",");
                turnoverList.append(",");
            }
        }
        return new TurnoverReportVO(dataList.toString(),turnoverList.toString());
    }

    @Override
    public UserReportVO userStatistics(DataOverViewQueryDTO dataOverViewQueryDTO) {
        LocalDate begin = dataOverViewQueryDTO.getBegin();//开始时间
        LocalDate end = dataOverViewQueryDTO.getEnd();//结束时间
        List<LocalDate> dates =new ArrayList<>();//用于盛放中间日期
        List<Integer> newUserList=new ArrayList<>();//用于盛放每日新增用户
        List<Integer> totalUserList=new ArrayList<>();//用于盛放每日用户
        int numsBefore=calculateUser(begin.minusDays(1));//前一日用户数量
        int numsToday=0;//今日用户数量

        //遍历起始日期到结束日期中间的日期，计算这些日期的用户数量和新增用户数量
        for(LocalDate date=begin; !date.isAfter(end);date=date.plusDays(1)) {//注意此处是浅拷贝，date和begin会同步变动！！
            dates.add(date);
            //计算当日的用户数量
            numsToday=calculateUser(date);
            totalUserList.add(numsToday);
            //使用当日用户数量减去前一日用户数量，得出新增用户数量
            newUserList.add(numsToday-numsBefore);
            //循环变量替换
            numsBefore=numsToday;
        }
        //使用StringUtils的join方法将集合中封装的数据转化为字符串
        return new UserReportVO(StringUtils.join(dates,','),StringUtils.join(totalUserList,','),StringUtils.join(newUserList,','));
    }

    @Override
    public OrderReportVO orderStatistics(DataOverViewQueryDTO dataOverViewQueryDTO) {
        LocalDate begin = dataOverViewQueryDTO.getBegin();//开始日期
        LocalDate end = dataOverViewQueryDTO.getEnd();//结束日期
        List<LocalDate> dates =new ArrayList<>();//用于盛放中间日期
        List<Integer> orderNums=new ArrayList<>();//用于盛放订单数
        List<Integer> validOrderNums=new ArrayList<>();//用于盛放有效订单数
        int sum=0;
        int validSum=0;
        double orderCompletionRate=0;
        for(LocalDate date=begin; !date.isAfter(end);date=date.plusDays(1)) {//注意此处是浅拷贝，date和begin会同步变动！！
            dates.add(date);
            //查询当天的订单总数
            int num=orderMapper.countByMap(prepareMap(date,date,null));
            sum+=num;
            orderNums.add(num);
            //查询当天的有效订单总数
            int validNum=orderMapper.countByMap(prepareMap(date,date,Orders.COMPLETED));
            validSum+=validNum;
            validOrderNums.add(validNum);
        }
        if(sum!=0) {//避免总订单数为0，计算订单完成率出错
            orderCompletionRate=(double)validSum/sum;
        }
        return OrderReportVO.builder().orderCompletionRate(orderCompletionRate)
                .dateList(StringUtils.join(dates,','))
                .orderCountList(StringUtils.join(orderNums,','))
                .validOrderCountList(StringUtils.join(validOrderNums,','))
                .totalOrderCount(sum)
                .validOrderCount(validSum).build();
    }

    @Override
    public SalesTop10ReportVO top10(DataOverViewQueryDTO dataOverViewQueryDTO) {
        LocalDate begin = dataOverViewQueryDTO.getBegin();//开始日期
        LocalDate end = dataOverViewQueryDTO.getEnd();//结束日期
        List<OrderDetail> orderDetailList=orderDetailMapper.top10(prepareMap(begin,end,Orders.COMPLETED));//用于盛放销量前十的菜品对应的订单详情数据
        log.info("查询获得的销量前十菜品:{}",orderDetailList);
        List<String> nameList=new ArrayList<>();//用于存放销量前十菜品名称
        List<Integer> numberList=new ArrayList<>();//用于存放销量前十菜品的对应的销量
        for (OrderDetail orderDetail : orderDetailList) {
            nameList.add(orderDetail.getName());
            numberList.add(orderDetail.getNumber());
        }
        return SalesTop10ReportVO.builder().nameList(StringUtils.join(nameList,','))
                .numberList(StringUtils.join(numberList,',')).build();
    }

    @Override
    public void export(HttpServletResponse response) {
        //获取营业数据
        LocalDate beginDate=LocalDate.now().minusDays(30);
        LocalDate endDate=LocalDate.now();
        BusinessDataVO businessDataVO30Days = workspaceService.businessData(LocalDateTime.of(beginDate,LocalTime.MIN), LocalDateTime.of(endDate,LocalTime.MIN));//开始时间，往前数30天的零点;结束时间，本日零点（只计算昨天的订单，今天的订单暂时不计算）

        //写入数据
        InputStream in=null;
        ServletOutputStream outputStream=null;
        XSSFWorkbook xssfWorkbook=null;
        try {
            in=this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");//获取类路径下的资源为输入流
            if(in!=null) {
                System.out.println(1);
                //通过POI技术向execl模板内写入数据
                xssfWorkbook=new XSSFWorkbook(in);//通过输入流获取模板文件并且创建一份新的
                XSSFSheet sheet1 = xssfWorkbook.getSheet("Sheet1");
                //填写30天的营业数据
                sheet1.getRow(1).getCell(1).setCellValue(beginDate+"至"+endDate);//填入时间区间
                XSSFRow row = sheet1.getRow(3);
                row.getCell(2).setCellValue(businessDataVO30Days.getTurnover());//填入30天内的营业额
                row.getCell(4).setCellValue(businessDataVO30Days.getOrderCompletionRate());//填入30天内的订单完成率
                row.getCell(6).setCellValue(businessDataVO30Days.getNewUsers());//填入30天内的新增用户数
                row=sheet1.getRow(4);
                row.getCell(2).setCellValue(businessDataVO30Days.getValidOrderCount());//填入30天内的有效订单数量
                row.getCell(4).setCellValue(businessDataVO30Days.getUnitPrice());//填入30天内的平均客单价
                //填写每天的明细数据
                row=sheet1.getRow(7);
                for(LocalDate date=beginDate;date.isBefore(endDate);date=date.plusDays(1)) {//从起始时间遍历到结束时间
                    BusinessDataVO businessDataVO = workspaceService.businessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));//查询该日期的营业数据VO
                    row.getCell(1).setCellValue(date.toString());//日期
                    row.getCell(2).setCellValue(businessDataVO.getTurnover());//营业额
                    row.getCell(3).setCellValue(businessDataVO.getValidOrderCount());//有效订单
                    row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());//订单完成率
                    row.getCell(5).setCellValue(businessDataVO.getUnitPrice());//平均客单价
                    row.getCell(6).setCellValue(businessDataVO.getNewUsers());//新增用户数
                    row=sheet1.getRow(row.getRowNum()+1);//切换到下一行
                }
                //将execl表格内容通过输出流写入http响应体
                outputStream = response.getOutputStream();
                xssfWorkbook.write(outputStream);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally { //关闭资源
            try {
                in.close();
                outputStream.close();
                xssfWorkbook.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 统计某个日期以及之前注册的用户数量
     * @param date 日期
     * @return 该日期的用户数量
     */
    private int calculateUser(LocalDate date) {
        Integer userCount=userMapper.countByCrtRange(null,LocalDateTime.of(date,LocalTime.MAX));
        if(userCount==null) {
            userCount=0;
        }
        return userCount;
    }

    /**
     * 统计某个日期的营业额
     * @param date 日期
     * @return 该日期的营业额
     */
    private BigDecimal calculateTurnover(LocalDate date) {
        BigDecimal turnover=orderMapper.sumByMap(prepareMap(date,date,Orders.COMPLETED));//获取营业额
        if(turnover==null) {
            turnover=new BigDecimal(0);//如果当天没有订单的话，返回的营业额将是null而不是0，画图会出现错误
        }
        return turnover;
    }

    /**
     * 准备用于mapper层查询的条件Map
     * @param beginDate 开始日期，查询条目在该日零点之后
     * @param endDate 结束日期，查询条目在该日23：59：59之前
     * @param status 状态
     * @return
     */
    private Map prepareMap(LocalDate beginDate,LocalDate endDate,Integer status) {
        Map map=new HashMap();
        map.put("beginTime", LocalDateTime.of(beginDate, LocalTime.MIN));
        map.put("endTime",LocalDateTime.of(endDate,LocalTime.MAX));
        map.put("status",status);
        return map;
    }
}
